import { storeKey } from "./injectKey.js";
import { reactive } from "vue";
import ModuleCollection from "./module/ModuleCollection.js";
import { isPromise, forEachValue } from "./utils.js";
import { watch } from "vue";
/**
 *  根据路径获取store上的最新状态
 * @param {*} state  模块状态 
 *  ["user", "profile", "name"]
 * const store = {
  user: {
    profile: {
      name: "贾公子",
      age: 25
    },
    settings: {
      theme: "dark",
      language: "zh-CN"
    }
  }
};
第一次 state 是store.user
第二次 state user.profile
第三次 state profile.name
reduce 第一个参数是 state 第二个参数是 key
 * @param {*} path  模块路径 ["user", "profile", "name"]
 * @returns 模块状态
 */
// 根据路径获取store上的最新状态
function getNestedState(state, path) {
  return path.reduce((state, key) => state[key], state);
}
function resetStoreState(store, state) {
  store._state = reactive({ data: state });
  const wrappedGetters = store._wrappedGetters;
  store.getters = {};

  // 遍历所有的getter
  forEachValue(wrappedGetters, (getter, key) => {
    // 代理到 store.getters 上
    Object.defineProperty(store.getters, key, {
      get: () => getter(store.state),
      enumerable: true, // 可枚举
    });
  });
  if (store.strict) {
    enableStrictMode(store);
  }
}

/**
 *  开启严格模式
 * @param {*} store  实例
 */
function enableStrictMode(store) {
  watch(
    () => store._state.data,
    () => {
      console.assert(store._committing, "在严格模式下，不能直接修改状态。");
    },
    { deep: true, flush: "sync" } //默认watch是异步的，这里设置为同步
  );
}
/**
 *  递归 安装模块
 * @param {*} store  实例
 * @param {*} state  模块状态
 * @param {*} path  模块路径
 * @param {*} module  模块实例
 */
function installModule(store, state, path, module) {
  let isRoot = path.length === 0;
  let namespace = store._modules?.getNamespace(path) || ""; // [a,c]

  // 非根模块，需要挂载到父模块上
  if (!isRoot) {
    // 假设当前 path 为 ["aCount","cCount"]，模拟执行顺序：
    // 1. path.slice(0, -1) 得到 ["aCount"]
    // 2. reduce 从根 state 开始，先取 state["aCount"]，得到 aCount 模块的 state 引用
    // 3. 将 module.state 挂载到 parentState（aCount 模块的 state 引用）["cCount"] 上，完成子模块状态挂载
    let parentState = path
      .slice(0, -1)
      .reduce((state, key) => state[key], state);
    store.withCommitting(() => {
      parentState[path[path.length - 1]] = module.state;
    });
  }
  // 统一调用模块内部自己的方法 取的是原始数据 this._raw.getters
  // 安装模块的getter
  module.forEachGetter((getter, key) => {
    store._wrappedGetters[namespace + key] = () => {
      // 如果直接使用模块自己的状态，不是响应式的
      return getter(getNestedState(store.state, path));
    };
  });
  // 安装模块的mutation
  module.forEachMutation((mutation, key) => {
    const entry =
      store._mutations[namespace + key] ||
      (store._mutations[namespace + key] = []);
    entry.push((payload) => {
      mutation.call(store, getNestedState(store.state, path), payload);
    });
  });
  // 安装模块的action action 返回一个promise
  module.forEachAction((action, key) => {
    const entry =
      store._actions[namespace + key] || (store._actions[namespace + key] = []);
    entry.push((payload) => {
      const res = action.call(store, store, payload);
      if (!isPromise(res)) {
        return Promise.resolve(res);
      }
      return res;
    });
  });

  //   递归安装子模块
  module.forEachChild((childModule, key) => {
    installModule(store, state, path.concat(key), childModule);
  });
}
export default class VuexStore {
  withCommitting(fn) {
    let store = this;
    // 包装函数，在调用mutation之前将_committing 设为true，调用完了设为false「切片」
    const committing = store._committing;
    store._committing = true;
    fn();
    store._committing = committing;
  }
  constructor(options) {
    const store = this;
    store._modules = new ModuleCollection(options);
    // (add:[fn,fn,fn]) 发布订阅
    store._wrappedGetters = Object.create(null);
    store._mutations = Object.create(null);
    store._actions = Object.create(null);
    // 调用的时候知道是 mutation 而且只能是同步操作
    store.strict = options.strict || false; //是不是 严格模式
    // 在mutation之前加个状态 _committing 为true
    // 调用 mutation 会改变 状态 ，监控这个状态，如果当前状态变化的时候_committing 为true 则代表是同步操作,
    // mutation调用完了，将_committing 设为false
    // 可以想象如果 值变化的时候为false，说明 mutation 调用完了 但是值变化了就不是同步操作了
    store._committing = false;

    // 定义状态
    const state = store._modules.root.state; //根状态
    installModule(store, state, [], store._modules.root);
    // 添加响应式
    resetStoreState(store, state);
    store._subscribers = [];
    const plugins = options.plugins;
    plugins.forEach((fn) => {
      fn(store);
    });
  }
  subscribe = (fn) => {
    this._subscribers.push(fn);
  };
  get state() {
    return this._state.data;
  }
  commit = (type, payload) => {
    const entry = this._mutations[type] || [];

    this.withCommitting(() => {
      entry.forEach((handler) => handler(payload));
    });
    this._subscribers.forEach((fn) => {
      fn({ type: type, payload }, this.state);
    });
  };
  dispatch = (type, payload) => {
    const entry = this._actions[type] || [];

    return Promise.all(entry.map((handler) => handler(payload)));
  };
  replaceState = (newState) => {
    this.withCommitting(() => {
      this._state.data = newState;
    });
  };

  install(app, injectKey) {
    // createApp(App).use(store, 'myStore')
    // provide 给app根组件一个，子组件向上查找
    // 把 store 实例提供给所有子组件，暴露的是store实例
    app.provide(injectKey || storeKey, this);
    //vue.property.$store vue2
    app.config.globalProperties.$store = this;
  }
  registerModule(path, rowModule) {
    const store = this;
    if (typeof path === "string") {
      path = [path];
    }
    // 要在原有的模块基础上增加
    let newModule = store._modules.register(rowModule, path);
    // 再把模块安装上
    installModule(store, store.state, path, newModule);
    // 重置状态
    resetStoreState(store, store.state);
  }
}
// 格式化用户的参数，将用户的参数转换为符合规范的参数
/**
 * root={
 * _raw:rootModule
 * state:rootModule.state
 * _children:{
 *      aCount:{
 *      _raw:aCountModule
 *      state:aCountModule.state
 *      _children:{
 *          cCount:{
 *              _raw:cCountModule
 *              state:cCountModule.state
 *            }
 *      }
 *     }
 *     bCount:{
 *      _raw:bCountModule
 *      state:bCountModule.state
 *     }
 * }
 */
